<!DOCTYPE html>
<html>
<head>
  <meta charset="UTF-8">
  <title>Document</title>
  <style>
    html,body{margin: 0;padding:0;}
  </style>
</head>
<body>
  <canvas class="cvs"></canvas>
</body>
<script>

  const cvs = document.querySelector(".cvs");
  cvs.width = document.documentElement.clientWidth;
  cvs.height = document.documentElement.clientHeight;

  const ctx = cvs.getContext("2d");

  // 两点之间能显示连线的最大距离
  const maxNum = 300;
  // 生成所有点的坐标信息（包含坐标x和y，及x和y的步长）
  const arr = [];
  for(let i=0;i<20;i++){
    arr.push({
      x: random(0, cvs.width-20),
      y: random(0, cvs.height-20),
      speedX: random(-5, 5),
      speedY: random(-5, 5)
    })
  }

  // 初始绘制
  action( arr );

  setInterval(()=>{
    // 遍历所有点的坐标信息
    arr.forEach(val=>{
      // 根据自身步长，改变位置
      val.x += val.speedX;
      val.y += val.speedY;
      // 边界限定
      if(val.x < 0){
        val.speedX = -val.speedX;
      }
      if(val.x > cvs.width){
        val.speedX = -val.speedX;
      }
      if(val.y < 0){
        val.speedY = -val.speedY;
      }
      if(val.y > cvs.height){
        val.speedY = -val.speedY;
      }
      // 重新绘制
      action( arr );
    })
  }, 30)


  function action( pos ){
    // 先清除之前的绘制
    ctx.clearRect(0, 0, cvs.width, cvs.height);
    // 画点
    for(let i=0;i<pos.length;i++){
      ctx.beginPath();
      ctx.arc(pos[i].x, pos[i].y, 6, 0, 2*Math.PI);
      ctx.fill();
    }
    // 所有点，两两连接
    for(let i=0;i<pos.length;i++){
      for(let j=0;j<pos.length;j++){
        ctx.beginPath();
        ctx.moveTo(pos[i].x, pos[i].y);
        ctx.lineTo(pos[j].x, pos[j].y);

        // 计算两点之间距离（利用勾股定理）
        const a = pos[j].x-pos[i].x;
        const b = pos[j].y-pos[i].y;
        const now = Math.sqrt(a*a + b*b);
        // 计算线的透明度
        const opacity = now > maxNum ? 0 : (1 - now/maxNum);
        ctx.strokeStyle = `rgba(0,0,0,${opacity})`;
        // 描边
        ctx.stroke();
      }
    }
  }

  function random(a,b){
    return Math.round(Math.random()*(a-b)+b);
  }

</script>
</html>